#!/usr/bin/env python

# Copyright 2011 - 2012 David Selby dave6502@gmail.com

# This file is part of FX Wizard.

# FX Wizard is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# FX Wizard is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with FX Wizard.  If not, see <http://www.gnu.org/licenses/>.

import win32gui, win32con, win32api, win32com, win32com.client
import os, os.path, time, subprocess
from PIL import Image, ImageGrab
from exception import WizardExcept

DEBUG = False

"""
A class offing an interface to screenshot and control of the MyMobiler screen,
it contains all the fun windows API calls :)

*NOTE*

The following code is usefull to show the current active window title, windows
is a learning curve.

while True:
	print ('Current active window handle : %s' % 
	win32gui.GetWindowText(win32gui.GetForegroundWindow()))
	time.sleep(2)
"""

class MyMobiler:

	def __init__(self, pos_x, pos_y):
		
		# position of the MyMobiler window
		self.offset_x = pos_x
		self.offset_y = pos_y

		# expected size of MyMobiler window
		self.win_x = 246
		self.win_y = 404

		# last cursor position
		self.cursor_x = 0
		self.cursor_y = 0

		self.handle_str = 'MyMobiler'
		self.handle  = 0 # used to move the MyMobiler window
		self.COM_obj = 0 # used to communicate with MyMobiler


	def show(self):

		"""
		Show MyMobiler window centered on the screen, do checks and raise
		exceptions if any issues
		"""

		self.handle = win32gui.FindWindow(None, self.handle_str)

		# check the window still exists
		if not self.handle:
			self.hide()
			raise WizardExcept('no_window')

		win32gui.SetWindowPos(self.handle, None, self.offset_x, self.offset_y, 0, 0, win32con.SWP_NOSIZE)
		win32gui.ShowWindow(self.handle, win32con.SW_RESTORE)
		win32gui.SetForegroundWindow(self.handle)

		self.COM_obj = win32com.client.Dispatch('WScript.Shell')
		self.COM_obj.AppActivate('MyMobiler')

		# bug workaround ... with one 'AppActivate' it is not possible to click
		# on the MyMobiler screen, 2 x 'AppActivate' and you can !, ... I am NEVER 
		# going to port this to windows 7 !

		self.COM_obj.AppActivate('MyMobiler')
		self.COM_obj.AppActivate('MyMobiler')

		x, y, x2, y2 = win32gui.GetWindowRect(self.handle)
		win_x = x2 - x
		win_y = y2 - y

		if not DEBUG:
			# check MyMobiler is connected to the PDA
			if self.checkLine((10, 258, 100, 0, 100)):
				self.hide()
				raise WizardExcept('black_window')

		# make sure its the correct size
		if win_x != self.win_x or win_y != self.win_y:
			self.hide()
			raise WizardExcept('wrong_size')


	def hide(self):

		"""
		Hide MyMobiler window, do checks and raise exceptions if any issues
		"""

		self.handle = win32gui.FindWindow(None, self.handle_str)

		# check the window still exists
		if not self.handle:
			raise WizardExcept('no_window')

		win32gui.ShowWindow(self.handle, win32con.SW_MINIMIZE)


	def grabImage(self, (x0, y0, x1, y1)):
		
		"""
		Grab a defined screen area and return a PIL image object. Parameters 
		are a tuple
		"""

		x0 += self.offset_x
		y0 += self.offset_y
		x1 += self.offset_x
		y1 += self.offset_y

		img = ImageGrab.grab((x0, y0, x1, y1))
		img = img.convert('L')

		return img

	
	def checkLine(self, (x, y, length, px_expect, pc)):

		"""
		Check a line of px against a 'px_expect' value, if >= pc match return 
		True, parameters are a tuple. This is not really the right place 
		for this method but lodging here until a better place can be found
		"""

		x0 = self.offset_x + x
		y0 = self.offset_y + y
		x1 = self.offset_x + x + length
		y1 = self.offset_y + y + 1

		img = ImageGrab.grab((x0, y0, x1, y1))
		img = img.convert('L')
		px_list = list(img.getdata())

		if DEBUG:
			print 'DEBUG my_mobiler checkLine: ', px_list

		# has a tolerance of 1 in case XP rendering code has noise !
		px_up = min(255, px_expect + 1)
		px_down = max(0, px_expect - 1)

		hits = float(len([ x for x in px_list if px_down <= x <= px_up]))

		return (hits / length * 100) >= pc


	def grabCtrl(self):

		"""
		Start controlling the MyMobiler screen, in reality just log the x, y
		so if the cursor moves an exception can be raised
		"""

		self.cursor_x, self.cursor_y = win32api.GetCursorPos()


	def releaseCtrl(self):

		"""
		Stop controlling the MyMobiler screen - for completeness 
		"""

		pass


	def click(self, coords):

		"""
		Click on the MyMobiler window and raise exceptions if any issues, coords
		are a tuple (x, y)
		"""

		# check to see if cursor is where I left it,
		cursor_x, cursor_y = win32api.GetCursorPos()

		if not DEBUG:
			if cursor_x != self.cursor_x or cursor_y !=  self.cursor_y:
				raise WizardExcept('csr_moved')

		self.cursor_x = coords[0] + self.offset_x
		self.cursor_y = coords[1] + self.offset_y

		if DEBUG:
			print 'DEBUG my_mobiler, clicking at: ', self.cursor_x, self.cursor_y 

		win32api.SetCursorPos((self.cursor_x, self.cursor_y))
		win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, self.cursor_x, self.cursor_y, 0, 0)
		win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, self.cursor_x, self.cursor_y, 0, 0)


	def sendText(self, text):

		"""
		Send text string to MyMobiler, deceptively simple
		"""

		self.COM_obj.SendKeys(text)


